/**
 * Software License, Version 1.0
 * 
 * Copyright 2003 The Trustees of Indiana University.  All rights reserved.
 * 
 *
 *Redistribution and use in source and binary forms, with or without 
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright 
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the 
 * following acknowledgement:
 *
 *"This product includes software developed by the Community Grids Lab. For 
 * further information contact the Community Grids Lab at 
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgement may appear in the software itself, and 
 * wherever such third-party acknowledgments normally appear.
 * 
 *4) The name Indiana University or Community Grids Lab or NaradaBrokering, 
 * shall not be used to endorse or promote products derived from this software 
 * without prior written permission from Indiana University.  For written 
 * permission, please contact the Advanced Research and Technology Institute 
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called NaradaBrokering, 
 * nor may Indiana University or Community Grids Lab or NaradaBrokering appear
 * in their name, without prior written permission of ARTI.
 * 
 *
 * Indiana University provides no reassurances that the source code provided 
 * does not infringe the patent or any other intellectual property rights of 
 * any other entity.  Indiana University disclaims any liability to any 
 * recipient for claims brought by any other entity based on infringement of 
 * intellectual property rights or otherwise.  
 *
 *LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO 
 *WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 *NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF 
 *INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS. 
 *INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", 
 *"VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.  
 *LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR 
 *ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION 
 *GENERATED USING SOFTWARE.
 */
package cgl.narada.benchmark.nbntp;

import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.LinkedList;

import cgl.narada.service.time.ntptime.NtpConnection;
import cgl.narada.service.time.ntptime.NtpInfo;
import cgl.narada.service.time.ntptime.NtpTimeService;

/**
 * Client class that measure the adjusted time difference with the server
 * specified.
 *
 * @author Hasan Bulut  - hbulut@indiana.edu
 */
public class ClientDiscrepancy extends Thread {
  //~ Instance fields ----------------------------------------------------------

  InetAddress clientAIP = null;
  LinkedList  lnkA    = new LinkedList();
  String      clientA = "kamet.ucs.indiana.edu";
  int         interval = 5000;
  private DataOutputStream dumpClientA = null;
  private String           dumpClientAFile = "DumpClientA.txt";
  private int              port            = 6123;

  //~ Constructors -------------------------------------------------------------

  /**
   * Generates a new ClientDiscrepancy object.
   */
  public ClientDiscrepancy() {
  }

  /**
   * Creates a new ClientDiscrepancy object.
   *
   * @param ca DOCUMENT ME!
   */
  public ClientDiscrepancy(String ca) {
    clientA = ca;
  }

  //~ Methods ------------------------------------------------------------------

  /**
   * DOCUMENT ME!
   */
  public void initialize() {
    try {
      clientAIP = InetAddress.getByName(clientA);
    } catch (UnknownHostException ex) {
      ex.printStackTrace();
    }
    if (dumpClientAFile != null) {
      try {
        FileOutputStream fosA = new FileOutputStream(dumpClientAFile);
        if (fosA != null) {
          dumpClientA = new DataOutputStream(fosA);
        }
      } catch (FileNotFoundException ex) {
        System.out.println("File cannot be opened. Exception occured:\n" + ex);
      }
    }
  }

  /**
   * DOCUMENT ME!
   *
   * @param argv DOCUMENT ME!
   *
   * @throws Exception DOCUMENT ME!
   */
  public static void main(String[] argv) throws Exception {
    String ca = "kamet.ucs.indiana.edu";
    if (argv.length == 2) {
      ca = argv[0];
    }
    NtpTimeService    ntpService = NtpTimeService.getInstance();
    ClientDiscrepancy CD = new ClientDiscrepancy(ca);
    System.out.println("NTP Time Service is started");
    ntpService.start();
    CD.initialize();
    System.out.println("ClientDiscrepancyr is started");
    CD.start();
  }

  /**
   * DOCUMENT ME!
   */
  public void run() {
    try {
      sleep(60 * 1000);
    } catch (InterruptedException ex3) {
    }
    boolean running      = true;
    int     counter1     = 0;
    int     counter2     = 0;
    long    noOfSamples  = 1;
    long    rndtripdelay = 10;
    try {
      NtpConnection ntpConnection1 = new NtpConnection(InetAddress.getByName(clientA),
                                                       port);
      System.out.println("sending ntp packet ");
      NtpInfo ntpInfo1 = ntpConnection1.getTSInfo();
      ntpConnection1.close();
      noOfSamples  = 1;
      rndtripdelay = ntpInfo1.roundTripDelay;
    } catch (IOException ex4) {
    }

    while (running) {
      counter1++;
      boolean received = false;
      int     counter3 = 0;

      while (!received && (counter3 <= 5)) {
        counter3++;
        NtpInfo ntpInfo = new NtpInfo();
        try {
          boolean running2 = true;
          while (running2) {
            NtpConnection ntpConnection = new NtpConnection(InetAddress.getByName(clientA),
                                                            port);
            System.out.println("sending ntp packet ");
            ntpInfo = ntpConnection.getTSInfo();
            ntpConnection.close();
            if (ntpInfo.roundTripDelay < (3 * rndtripdelay)) {
              running2 = false;
            } else {
              try {
                sleep(1000);
                System.out.println("round trip delay is high. trying again:" +
                                   ntpInfo.roundTripDelay + "\t" +
                                   rndtripdelay);
                rndtripdelay = rndtripdelay + 1;
                running2     = true;
              } catch (InterruptedException ex5) {
              }
            }
          }
          rndtripdelay = (noOfSamples * rndtripdelay + ntpInfo.roundTripDelay) / (noOfSamples +
                         1);
          noOfSamples++;
          received = true;
          NTPValues tv = new NTPValues();
          tv.originate      = ntpInfo.originate;
          tv.receive        = ntpInfo.receive;
          tv.transmit       = ntpInfo.transmit;
          tv.reception      = ntpInfo.reception;
          tv.roundTripDelay = ntpInfo.roundTripDelay;
          tv.offset         = ntpInfo.offset;
          lnkA.add(tv);
        } catch (IOException ex) {
          ex.printStackTrace();
          received = false;
          NTPValues tv = new NTPValues();
          lnkA.add(tv);
        }
      }
      if (!received) {
        System.out.println("WARNING: cannot receive packets from " + clientA);
      }
      received = false;
      counter3 = 0;
      if (counter1 % 5 == 0) {
        counter1 = 0;
        int lnsize = lnkA.size();
        for (int i = 0; i < lnsize; i++) {
          String temp = ((NTPValues) (lnkA.get(i))).toString();
          try {
            dumpClientA.writeBytes(temp);
          } catch (IOException ex1) {
          }
        }
        lnkA.clear();
      }
      try {
        sleep(interval);
      } catch (InterruptedException ex2) {
      }
    }
  }

  //~ Inner Classes ------------------------------------------------------------
  /**
   * A data structure to keep sample values.
   * @author Hasan Bulut  - hbulut@indiana.edu
   */
  public class NTPValues {
    //~ Instance fields --------------------------------------------------------

    public long offset    = 0;
    public long originate = 0;
    public long receive   = 0;
    public long reception      = 0;
    public long roundTripDelay = 0;
    public long transmit       = 0;

    //~ Methods ----------------------------------------------------------------
    /**
     *
     * @return String - String representation of the sample values
     */
    public String toString() {
      String temp = originate + "\t" + receive + "\t" + transmit + "\t" +
                    reception + "\t" + roundTripDelay + "\t" + offset + "\n";
      return temp;
    }
  }
}
